package sz.com.postoffice.jxyzwgcase.service.impl;

import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import sz.com.postoffice.jxyzwgcase.core.PageInfo;
import sz.com.postoffice.jxyzwgcase.dao.node.OneClickDao;
import sz.com.postoffice.jxyzwgcase.service.OneClickService;
import sz.com.postoffice.jxyzwgcase.utils.ExcelUtils;

import javax.annotation.Resource;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class OneClickServiceImpl implements OneClickService {
    //标题字典和顺序
    private HashMap<String, String> dictionary;

    {
        dictionary = new LinkedHashMap<>();
        dictionary.put("cityName", "市");
        dictionary.put("countyName", "区县");
        dictionary.put("deptName", "营业部");
        dictionary.put("emp_dept_name", "营业部");
        dictionary.put("nodeTotal", "工单总数");
        dictionary.put("processedPassRate", "及时处理率");
        dictionary.put("untreatedSummary", "未处理总数");
        dictionary.put("untreatedTimeoutTotal", "未处理超时数");

        dictionary.put("emp_name", "当前处理人");
        dictionary.put("START_TIME_", "当前节点到达时间");
        dictionary.put("mailNo", "邮件号");
        dictionary.put("questionMeno", "问题描述");
        dictionary.put("processed", "是否处理完成");
    }

    @Resource
    private OneClickDao oneClickDao;

    @Override
    public PageInfo findProcessingTimeoutDetail(Map map) {
        List<String> procEdfId = (List<String>) map.get("proc_edf_ID");
        map.put("proc_edf_ID", getProcDdf(procEdfId));
        Long totalRow = oneClickDao.findUntreatedTimeoutDetailTotal4page(map);
        Integer[] integers = processPage(map, totalRow);
        map.put("startIndex", integers[0]);
        map.put("pageSize", integers[1]);
        List<Map> timeoutDetail = oneClickDao.findUntreatedTimeoutDetail(map);
        if (CollectionUtils.isEmpty(timeoutDetail)) return null;

        List<String> procInstIdList = timeoutDetail.stream().map(p -> p.get("PROC_INST_ID_").toString()).distinct().collect(Collectors.toList());
        List<Map> mailDetailByProcInstID = oneClickDao.findMailDetailByProcInstID(procInstIdList);
        for (Map processDetail : timeoutDetail) {
            /**
             * 处理时间差16小时，入参时间在sql语句中添加。把本地时区设置为0时区测试
             */
            if (processDetail.containsKey("START_TIME_")) {
                Date startTime = (Date) processDetail.get("START_TIME_");
                processDetail.put("START_TIME_", formatDate(startTime));
            }
            if (processDetail.containsKey("END_TIME_")) {
                Date endTime = (Date) processDetail.get("END_TIME_");
                processDetail.put("END_TIME_", formatDate(endTime));
            }
            //添加mailDetail信息
            for (Map mailDetail : mailDetailByProcInstID) {
                if (mailDetail.get("process_id").equals(processDetail.get("PROC_INST_ID_"))) {
                    processDetail.putAll(mailDetail);
                    break;
                }
            }
            //员工名字为空
            if (!processDetail.containsKey("emp_name")) processDetail.put("emp_name", "员工名称未录入");
        }

        PageInfo pageInfo = new PageInfo();
        pageInfo.setCurrentPage(integers[2]);
        pageInfo.setPageSize(integers[1]);
        pageInfo.setStartIndex(integers[0]);
        pageInfo.setTotalRow(totalRow);
        pageInfo.setList(timeoutDetail);

        return pageInfo;
    }

    @Override
    public List<Map> findProcessingTimeoutSummary(Map map) {
        map.put("proc_edf_ID", getProcDdf((List<String>) map.get("proc_edf_ID")));

        List<Map> totalMap = oneClickDao.findProcessedAndUntreatedSummary(map);//总数
        List<Map> untreatedSummary = oneClickDao.findUntreatedSummary(map);//未处理的总数
        List<Map> untreatedTimeoutTotal = oneClickDao.findUntreatedTimeoutTotal(map);//未处理超时总数
        List<Map> processedPassTotal = oneClickDao.findProcessedPassTotal(map);//及时处理（24小时处理完成）总数

        //根据分组依据合并数据
        for (Map node : totalMap) {
            if (node.containsKey("deptCode")) {
                processedPassRate(processedPassTotal, node, "deptCode");
                increaseField(node, untreatedSummary, "deptCode", "untreatedSummary");
                increaseField(node, untreatedTimeoutTotal, "deptCode", "untreatedTimeoutTotal");
            } else if (node.containsKey("countyCode")) {
                processedPassRate(processedPassTotal, node, "countyCode");
                increaseField(node, untreatedSummary, "countyCode", "untreatedSummary");
                increaseField(node, untreatedTimeoutTotal, "countyCode", "untreatedTimeoutTotal");
            } else {
                processedPassRate(processedPassTotal, node, "cityCode");
                increaseField(node, untreatedSummary, "cityCode", "untreatedSummary");
                increaseField(node, untreatedTimeoutTotal, "cityCode", "untreatedTimeoutTotal");
            }
        }

        return totalMap;
    }

    @Override
    public XSSFWorkbook findProcessingTimeoutSummaryToExcel(Map map) {
        XSSFWorkbook excel = ExcelUtils.getExcel();
        XSSFSheet sheet = excel.createSheet("处理及时率统计");
        List<Map> timeoutSummary = findProcessingTimeoutSummary(map);

        if (CollectionUtils.isEmpty(timeoutSummary)) return excel;
        List<String> sheetTable = getSheetTableByMap4Summary(timeoutSummary.get(0));
        Integer rowNum = 0;
        rowNum = excelWriteRow(sheet, sheetTable, rowNum);

        for (Map insertRowMap : timeoutSummary) {
            rowNum = excelWriteRow(sheet, getRowValueByMap(insertRowMap), rowNum);
        }
        //设置自动列宽
        ExcelUtils.autoSizeColumn(sheet, sheetTable.size());
        return excel;
    }

    @Override
    public XSSFWorkbook findProcessingTimeoutDetailToExcel(Map map) {
        XSSFWorkbook excel = ExcelUtils.getExcel();
        XSSFSheet sheet = excel.createSheet("未处理详细情况");

        map.put("proc_edf_ID", getProcDdf((List<String>) map.get("proc_edf_ID")));
        List<Map> timeoutDetail = oneClickDao.findUntreatedTimeoutDetail(map);
        if (CollectionUtils.isEmpty(timeoutDetail)) return excel;
        List<String> procInstIdList = timeoutDetail.stream().map(p -> p.get("PROC_INST_ID_").toString()).distinct().collect(Collectors.toList());
        List<Map> mailDetailByProcInstID = oneClickDao.findMailDetailByProcInstID(procInstIdList);
        for (Map processDetail : timeoutDetail) {
            /**
             * 处理时间差16小时，入参时间在sql语句中添加。把本地时区设置为0时区测试
             */
            if (processDetail.containsKey("START_TIME_")) {
                Date startTime = (Date) processDetail.get("START_TIME_");
                processDetail.put("START_TIME_", formatDate(startTime));
            }
            if (processDetail.containsKey("END_TIME_")) {
                Date endTime = (Date) processDetail.get("END_TIME_");
                processDetail.put("END_TIME_", formatDate(endTime));
            }

            //添加mailDetail信息
            for (Map mailDetail : mailDetailByProcInstID) {
                if (mailDetail.get("process_id").equals(processDetail.get("PROC_INST_ID_"))) {
                    processDetail.putAll(mailDetail);
                    break;
                }
            }
            //汉化是否
            if ("1".equals(processDetail.get("processed").toString())) {
                processDetail.put("processed", "是");
            } else {
                processDetail.put("processed", "否");
            }
        }
        Map table = timeoutDetail.get(0);
        if (!table.containsKey("emp_name")) table.put("emp_name", "员工名称未录入");

        List<String> sheetTable = getSheetTableByMap4Summary(table);
        Integer rowNum = 0;
        rowNum = excelWriteRow(sheet, sheetTable, rowNum);

        for (Map insertRowMap : timeoutDetail) {
            if (!insertRowMap.containsKey("emp_name")) insertRowMap.put("emp_name", "员工名称未录入");
            rowNum = excelWriteRow(sheet, getRowValueByMap(insertRowMap), rowNum);
        }
        //设置自动列宽
        ExcelUtils.autoSizeColumn(sheet, sheetTable.size());
        return excel;
    }

    private Integer[] processPage(Map map, Long totalRow) {
        Integer startIndex = 0;
        Integer pageSize = 10;
        Integer currentPage = 1;
        if (map.containsKey("currentPage") && map.containsKey("pageSize")) {
            pageSize = Integer.valueOf(map.get("pageSize").toString());
            currentPage = Integer.valueOf(map.get("currentPage").toString());
        } else if (map.containsKey("pageSize")) {
            pageSize = Integer.valueOf(map.get("pageSize").toString());
        }
        startIndex = (currentPage - 1) * pageSize;
        return new Integer[]{startIndex, pageSize, currentPage};
    }

    /**
     * 写入一行数据返回下一行行号
     *
     * @param sheet
     * @param sheetTable
     * @param rowNum
     * @return
     */
    private Integer excelWriteRow(XSSFSheet sheet, List<String> sheetTable, Integer rowNum) {
        int i = 0;
        for (String cellValue : sheetTable) {
            ExcelUtils.writeCellValue(sheet, rowNum, i++, cellValue);
        }
        return rowNum + 1;
    }

    /**
     * 根据map的keys生成标题列数据
     *
     * @param map
     * @return
     */
    private List<String> getSheetTableByMap4Summary(Map map) {
        List<String> list = new ArrayList<>();
        for (String key : dictionary.keySet()) {
            if (map.containsKey(key)) {
                list.add(dictionary.get(key));
            }
        }
        return list;
    }

    /**
     * 根据map的keys生成行数据
     *
     * @return
     */
    private List<String> getRowValueByMap(Map map) {
        List<String> list = new ArrayList<>();
        for (String key : dictionary.keySet()) {
            if (map.containsKey(key)) {
                list.add(map.get(key).toString());
            }
        }
        return list;
    }

    /**
     * 处理流程没有传流程名称的情况，防止其他流程信息外泄
     *
     * @param procEdfId
     * @return
     */
    private List<String> getProcDdf(List<String> procEdfId) {
        List<String> proc_edf_id;
        if (procEdfId != null && procEdfId.size() > 0) {
            proc_edf_id = procEdfId;
        } else {
            proc_edf_id = new ArrayList<>();
            proc_edf_id.add("邮件直查");
            proc_edf_id.add("揽收营销");
            proc_edf_id.add("投递质量");
        }
        return getProcDdfID(proc_edf_id);
    }

    /**
     * 根据流程名称查询激活流程实例id
     *
     * @param procName
     * @return
     */
    public List<String> getProcDdfID(List<String> procName) {
        List<String> returnList = new ArrayList<>();
        List<Map> activityProcessing = oneClickDao.getAllActivityProcessing();
        for (Map map : activityProcessing) {
            for (String s : procName) {
                if (map.get("process_define_name").toString().contains(s)) {
                    returnList.add(map.get("process_define_id").toString());
                    break;
                }
            }
        }
        return returnList;
    }

    /**
     * 给node(Map)里面添加字段,通过传入的mapList和groupField确认插入位置，increaseField为需要添加的字段
     *
     * @param node
     * @param mapList
     * @param groupField
     * @param increaseField
     */
    private void increaseField(Map node, List<Map> mapList, String groupField, String increaseField) {
        String groupString = node.get(groupField).toString();
        for (Map map : mapList) {
            if (groupString.equals(map.get(groupField).toString())) {
                node.put(increaseField, map.get(increaseField));
                return;
            }
        }
        if (!node.containsKey(increaseField)) node.put(increaseField, 0);
    }

    /**
     * nodeMap计算及时处理率百分比
     *
     * @param mapList
     * @param node
     * @param groupField
     */
    private void processedPassRate(List<Map> mapList, Map node, String groupField) {
        String groupString = node.get(groupField).toString();
        for (Map map : mapList) {
            if (groupString.equals(map.get(groupField).toString())) {
                node.put("processedPassRate", divisionObj(map.get("processedPassTotal"), node.get("nodeTotal")));
                return;
            }
        }
        if (!node.containsKey("processedPassRate")) node.put("processedPassRate", "0%");
    }

    /**
     * 计算百分比
     *
     * @param a
     * @param b
     * @return
     */
    private String divisionObj(Object a, Object b) {
        Integer integerA = Integer.valueOf(a.toString());
        Integer integerB = Integer.valueOf(b.toString());
        if (integerB == 0) {
            return "0%";
        }
        NumberFormat numberFormat = NumberFormat.getInstance();
        numberFormat.setMaximumFractionDigits(2);
        return numberFormat.format((float) integerA / (float) integerB * 100) + "%";
    }

    /**
     * 增加16个小时
     *
     * @param date
     * @return
     */
    private static String formatDate(Date date) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Calendar ca = Calendar.getInstance();
        ca.setTime(date);
        ca.add(Calendar.HOUR_OF_DAY, 16);
        return formatter.format(ca.getTime());
    }

    public static List<Map<String, Object>> mergeList(List<Map<String, Object>> m1, List<Map<String, Object>> m2, String mergeListFiled) {
        m1.addAll(m2);
        Set<String> set = new HashSet<>();
        return m1.stream()
                .collect(Collectors.groupingBy(o -> {
                    //暂存所有key
                    set.addAll(o.keySet());
                    //按a_id分组
                    return o.get(mergeListFiled);
                })).entrySet().stream().map(o -> {

                    //合并
                    Map<String, Object> map = o.getValue().stream().flatMap(m -> {
                        return m.entrySet().stream();
                    }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b));

                    //为没有的key赋值0
                    set.stream().forEach(k -> {
                        if (!map.containsKey(k)) {
                            map.put(k, 0);
                        }
                    });

                    return map;
                }).collect(Collectors.toList());
    }
}
